package com.thinkit.cms.service.category;

import cn.hutool.core.io.FileUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.thinkit.cms.api.category.*;
import com.thinkit.cms.api.content.ContentService;
import com.thinkit.cms.api.model.ModelService;
import com.thinkit.cms.api.site.SiteService;
import com.thinkit.cms.api.site.SiteTemplateService;
import com.thinkit.cms.config.ActuatorComponent;
import com.thinkit.cms.dto.category.CategoryAttrDto;
import com.thinkit.cms.dto.category.CategoryDto;
import com.thinkit.cms.dto.category.CategoryNavbar;
import com.thinkit.cms.dto.category.CategoryTemplateDto;
import com.thinkit.cms.entity.category.Category;
import com.thinkit.cms.mapper.category.CategoryMapper;
import com.thinkit.cms.strategy.checker.CategoryChecker;
import com.thinkit.core.annotation.SiteMark;
import com.thinkit.core.base.BaseServiceImpl;
import com.thinkit.core.constant.Channel;
import com.thinkit.core.constant.Constants;
import com.thinkit.core.handler.CustomException;
import com.thinkit.processor.message.MessageSend;
import com.thinkit.utils.enums.ActionEnum;
import com.thinkit.utils.enums.PathRule;
import com.thinkit.utils.model.*;
import com.thinkit.utils.properties.ThinkItProperties;
import com.thinkit.utils.utils.BuildTree;
import com.thinkit.utils.utils.Checker;
import com.thinkit.utils.utils.Md5;
import com.thinkit.utils.utils.ModelFieldUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 分类 服务实现类
 * </p>
 *
 * @author lg
 * @since 2020-08-07
 */
@Transactional
@Service
public class CategoryServiceImpl extends BaseServiceImpl<CategoryDto, Category, CategoryMapper> implements CategoryService {


    @Autowired
    ThinkItProperties thinkItProperties;

    @Autowired
    ModelService modelService;

    @Autowired
    CategoryModelService categoryModelService;

    @Autowired
    CategoryAttrService categoryAttrService;

    @Autowired
    CategoryModelRelationService categoryModelRelationService;

    @Autowired
    CategoryTemplateService categoryTemplateService;

    @Autowired
    SiteTemplateService siteTemplateService;

    @Autowired
    ContentService contentService;

    @Autowired
    SiteService siteService;

    @Autowired
    MessageSend messageSend;

    @Autowired
    ActuatorComponent actuatorComponent;


    @Override
    public PageDto<CategoryDto> listPage(PageDto<CategoryDto> pageDto){
        IPage<CategoryDto> pages = new Page<>(pageDto.getPageNo(), pageDto.getPageSize());
        IPage<CategoryDto> result = baseMapper.listPage(pages, pageDto.getDto());
        PageDto<CategoryDto> resultSearch = new PageDto(result.getTotal(), result.getPages(), result.getCurrent(), Checker.BeNotEmpty(result.getRecords()) ? result.getRecords() : Lists.newArrayList());
        return resultSearch;
    }



    @Override
    public Tree<CategoryDto> treeCategory() {
        List<Tree<CategoryDto>> trees  = baseMapper.treeCategory(getSiteId());
        List<Tree<CategoryDto>> topNodes = BuildTree.buildList(trees, "0");
        Tree<CategoryDto> root = new Tree<CategoryDto>();
        root.setKey("0").setId("0").setParentId("0").setHasParent(false).setChildren(topNodes).setTitle("父分类");
        return root;
    }

    @Override
    public Tree<CategoryDto> treeCategoryForContent() {
        List<CategoryDto> categoryDtos  = baseMapper.treeCategoryForContent(getSiteId(true));
        if(Checker.BeNotEmpty(categoryDtos)){
            List<Tree<CategoryDto>> trees=filterBuild(categoryDtos);
            List<Tree<CategoryDto>> topNodes = BuildTree.buildList(trees, "0");
            Tree<CategoryDto> root = new Tree<CategoryDto>();
            root.setKey("0").setId("0").setParentId("0").setHasParent(false).setChildren(topNodes).setTitle("父分类");
            return root;
        }
        return null;
    }

    @Override
    public String getCode(String categoryId) {
        return baseMapper.getCode(categoryId);
    }

    @Override
    public List<CategoryNavbar> navbar(String code) {
        List<CategoryNavbar> categorys = baseMapper.navbar(getSiteId());
        return build(categorys,code);
    }

    @Override
    public Map<String, Object> loadTempParams(String categoryId) {
        Map<String, Object> mapDetail =baseMapper.getMapDetail(categoryId,null,null);
        ModelFieldUtil.formatData(mapDetail);
        loadTempPath(mapDetail,categoryId);
        //setPage(mapDetail);
        return mapDetail;
    }

    @Override
    public Map<String, Object> info(String code, String id) {
        Map<String,Object> params = new HashMap<>();
        Map<String,Object>  categoryInfo =baseMapper.getMapDetail(id,code,getSiteId());
        ModelFieldUtil.formatData(categoryInfo);
        params.put("this",categoryInfo);
        if(Checker.BeBlank(id)) id = categoryInfo.get("id").toString();
        List<Map<String, Object>> childs = childinfo(id); // 子分类
        params.put("childs",childs);
        String parentId = categoryInfo.get("parentId").toString();
        if(Checker.BeNotBlank(parentId) && !"0".equals(parentId)){
            Map<String,Object>  parentInfo =baseMapper.getMapDetail(parentId,null,getSiteId());
            params.put("parent",parentInfo);
        }
        return params;
    }

    @Override
    public List<Map<String, Object>> childinfo(String id) {
        List<Map<String,Object>> categorysInfo =baseMapper.getMapDetails(id,getSiteId());
        if(Checker.BeNotEmpty(categorysInfo)){
            for(Map<String,Object> category:categorysInfo){
                ModelFieldUtil.formatData(category);
            }
        }
        return Checker.BeNotEmpty(categorysInfo)?categorysInfo:Lists.newArrayList();
    }

    @Override
    public void genCategory(String categoryId) {
        Map<String,Object> params = this.loadTempParams(categoryId);
        actuatorComponent.execute(categoryId, ActionEnum.GENERATE_CATEGORY,params);
    }

    @Override
    public List<String> getIds() {
        List<String> ids = baseMapper.getIds(getSiteId());
        return Checker.BeNotEmpty(ids)?ids:Lists.newArrayList();
    }

    @Override
    public List<CategoryDto>  breadCrumbs(String id, String postfix,Boolean containit) {
        List<CategoryDto> categoryDtos = new ArrayList<>();
        CategoryDto categoryDto = baseMapper.getDetail(id);
        if(containit){
            categoryDtos.add(categoryDto);
        }
        if(!"0".equals(categoryDto.getParentId())){
            categoryLoop(categoryDto.getParentId(),postfix,categoryDtos);
        }
        return categoryDtos;
    }

    private void categoryLoop(String id,String postfix,List<CategoryDto> categoryDtos){
        CategoryDto categoryDto = baseMapper.getDetail(id);
        if(Checker.BeNotNull(categoryDto)){
            categoryDto.setName(categoryDto.getName()+postfix);
            categoryDtos.add(categoryDto);
            if(!"0".equals(categoryDto.getParentId())){
                categoryLoop(categoryDto.getParentId(),postfix,categoryDtos);
            }
        }
    }


    private void loadTempPath(Map<String, Object> mapDetail,String categoryId){
         String templatePath =categoryTemplateService.getTempPathByCategoryId(categoryId,getSiteId());
         String sitePath = siteService.getDestPath(false,getSiteId(true));
         String destPath = mapDetail.get("path").toString();
         mapDetail.put(Channel.TEMP_PATH,templatePath);
         mapDetail.put(Channel.DEST_PATH,sitePath+destPath);
         mapDetail.put(Channel.DEST_PATH_COPY,sitePath+destPath);
         mapDetail.put(Channel.FRIST_PATH,mapDetail.get("path"));

    }

    private void setPage(Map<String,Object> objectMap){
        Map<String,Object> pageParam = new HashMap<>();
        objectMap.put("pageNo",1);
        objectMap.put("pageSize",objectMap.get("pageSize"));
        objectMap.put("total",0);
        objectMap.put("totalPage",1);
        objectMap.put("contents",Lists.newArrayList());
    }


    @Override
    public Integer queryPageSize(String code, String categoryId) {
        Integer pageSize = baseMapper.queryPageSize(code,categoryId);
        return Checker.BeNotNull(pageSize)?pageSize:10;
    }


    private List<CategoryNavbar> build(List<CategoryNavbar> nodes, String code) {
        if (nodes == null) {
            return null;
        }
        List<CategoryNavbar> topNodes = new ArrayList<CategoryNavbar>();
        for (CategoryNavbar  navbar: nodes) {
            formatJsonMap(navbar);
            String pid = navbar.getParentId();
            if (pid == null || "0".equals(pid)) {
                if(Checker.BeNotBlank(code)){
                    if(code.equals(navbar.getCode())){
                        topNodes.add(navbar);
                        continue;
                    }
                }else{
                    topNodes.add(navbar);
                    continue;
                }
            }
            for (CategoryNavbar parent : nodes) {
                String id = parent.getId();
                if (id != null && id.equals(pid)) {
                    parent.getChildren().add(navbar);
                    parent.setHasChildren(true);
                    continue;
                }
            }
        }
        return topNodes;
    }

    private void formatJsonMap(CategoryNavbar  navbar){
        if(Checker.BeNotBlank(navbar.getData())){
            Map param = new HashMap();
            param.put("data",navbar.getData());
            ModelFieldUtil.formatData(param);
            navbar.setExt(param);
        }
    }

    private  List<Tree<CategoryDto>> filterBuild(List<CategoryDto> categoryDtos){
        List<Tree<CategoryDto>> trees = new ArrayList<Tree<CategoryDto>>();
        for (CategoryDto categoryDto : categoryDtos) {
            Tree<CategoryDto> tree = new Tree<CategoryDto>();
            tree.setId(categoryDto.getId());
            tree.setKey(categoryDto.getId());
            tree.setParentId(categoryDto.getParentId());
            tree.setTitle(categoryDto.getName());
            tree.setSpread(true);
            Map<String, Object> attributes = new HashMap<>(16);
            attributes.put("singlePage", categoryDto.getSinglePage());
            attributes.put("allowContribute", categoryDto.getAllowContribute());
            attributes.put("hidden", categoryDto.getHidden());
            tree.setAttributes(attributes);
            trees.add(tree);
        }
        return trees;
    }

    @Override
    public Map<String, Object> loadTemplate() {
        List<TreeFileModel> templateTree = modelService.loadTemplateTree();
        Map<String,Object> result = new HashMap<>();
        result.put("templateFiles",templateTree);
//        SiteTemplateDto siteTemplateDto = loadDefaultTemplate();
//        if(Checker.BeNotNull(siteTemplateDto)){
//            result.put("templateId",siteTemplateDto.getTemplateId());
//        }
        return result;
    }

    @Override
    public void deleteCategoryModel(String categoryModelId) {
        if(Checker.BeNotBlank(categoryModelId)){
            baseMapper.deleteCategoryModel(categoryModelId);
        }
    }

    @SiteMark
    @Transactional
    @Override
    public ApiResult save(CategoryDto v) {
        checkLegal(v);
        super.insert(v);
        saveUpdatetAttr(v,true);
        saveUpdateTemplate(v,true);
        actuatorComponent.execute(v.getId(),ActionEnum.CREATE_CATEGORY,v,new CategoryChecker(v));
        return ApiResult.result(v.getId());
    }

    @Transactional
    @Override
    public void update(CategoryDto v) {
        checkLegal(v);
        super.updateByPk(v);
        saveUpdatetAttr(v,false);
        saveUpdateTemplate(v,false);
        actuatorComponent.execute(v.getId(),ActionEnum.UPDATE_CATEGORY,v,new CategoryChecker(v));
    }

    @Transactional
    @Override
    public boolean deleteById(String id) {
        checkLegal(id);
        categoryAttrService.deleteByFiled("category_id",id);
        categoryModelRelationService.deleteByFiled("category_id",id);
        categoryTemplateService.deleteByFiled("category_id",id);
        List<String> contentIds = baseMapper.getContentIds(id);
        if(Checker.BeNotEmpty(contentIds)){
            deleteBatchContent(id,contentIds);
        }
        boolean res = super.deleteByPk(id);
        actuatorComponent.execute(id,ActionEnum.DELETE_CATEGORY,id);
        return res;
    }

    private void deleteBatchContent(String id,List<String> contentIds){
        int countNum = contentIds.size();//总数
        if(countNum>1000){
            int multipleNum=countNum/1000;//倍数
            int remainderNum=countNum%1000;//余数
            int i=0;
            for(;i<multipleNum;i++){
                List<String> subCids = contentIds.subList(i*1000, (i+1)*1000);
                contentService.deleteAllByCategoryId(id,subCids);
            }
            if(remainderNum>0){
                List<String> subCids = contentIds.subList(i*1000, (i*1000+remainderNum));
                contentService.deleteAllByCategoryId(id,subCids);
            }
        }else{
            contentService.deleteAllByCategoryId(id,contentIds);
        }
    }

    @Override
    public  Map<String,Object> getDetail(String id) {
        Map<String,Object> result = new HashMap<>();
        CategoryDto categoryDto = baseMapper.getDetail(id);
        result.put("category",categoryDto);
        handModels(categoryDto,result);
        handSetTemplate(id,result);
        return result;
    }

    @Override
    public CategoryDto getDetailById(String id) {
          CategoryDto categoryDto = baseMapper.getDetail(id);
          return categoryDto;
    }

    @Override
    public List<CategoryDto> listCategoryByPid(String categoryId) {
        List<CategoryDto> cmsCategoryDtos =baseMapper.listCategoryByPid(categoryId);
        return Checker.BeNotEmpty(cmsCategoryDtos)? cmsCategoryDtos:Lists.newArrayList();
    }


    private void handSetTemplate( String id,Map<String,Object> result){
        String templatePath = baseMapper.getCategoryTempPath(id,getSiteId());
        if(Checker.BeNotBlank(templatePath) ){
            String finaPath = thinkItProperties.getTemplate()+templatePath;
            result.put("templatePath",templatePath);
            if (FileUtil.exist(finaPath)){
                String md5Key = Md5.md5(new File(finaPath).getAbsolutePath().replace("\\","/"));
                result.put("md5Key",md5Key);
            }
        }
    }

    private void handModels(CategoryDto categoryDto,Map<String,Object> result){
        boolean isExtend = Checker.BeNotBlank(categoryDto.getCategoryModelId());
        if(isExtend){
            String designField =categoryModelService.getDesignField(categoryDto.getCategoryModelId());
            List<DynamicModel> dynamicModels =ModelFieldUtil.jsonStrToModel(categoryDto.getData());
            List<DynamicModel> designModels =ModelFieldUtil.jsonStrToModel(designField);
            ModelFieldUtil.copyField(designModels,dynamicModels);
            result.put("models",Checker.BeNotEmpty(designModels)?designModels:Lists.newArrayList());
        }
    }


    private void saveUpdateTemplate(CategoryDto v, boolean save){
        // 查询当前网站的默认模板
        if(!v.getOnlyUrl()){
            if(save){
                saveTemplate(v);
            }else{
                checkIsSaveOrUp(v);
            }
        }
    }

    private void checkIsSaveOrUp(CategoryDto v){
        String templateId = siteTemplateService.findTemplateId(getSiteId());
        CategoryTemplateDto categoryTemplate=categoryTemplateService.getCategoryTemp(v.getId(),getSiteId(),templateId);
        if(Checker.BeNotNull(categoryTemplate)){
            String tempPath = v.getTemplatePath().replace("\\", Constants.SEPARATOR);
            if(!tempPath.equals(categoryTemplate.getTemplatePath())){
                categoryTemplate.setTemplatePath(tempPath);
                categoryTemplateService.updateByPk(categoryTemplate);
            }
        }else{
            saveTemplate(v);
        }
    }

    private void saveTemplate(CategoryDto v){
        String templateId = siteTemplateService.findTemplateId(getSiteId());
        CategoryTemplateDto templateDto =new CategoryTemplateDto();
        templateDto.setCategoryId(v.getId()).setSiteId(getSiteId()).setTemplateId(templateId).
        setTemplatePath(v.getTemplatePath().replace("\\", Constants.SEPARATOR));
        categoryTemplateService.insert(templateDto);
    }

    private void checkLegal(String id){
        if("0".equals(id)){
            throw new CustomException(ApiResult.result(5034));
        }
        if(checkHasChild(id)){
            throw new CustomException(ApiResult.result(5033));
        }
        if(queryHasContent(id)){
            throw new CustomException(ApiResult.result(5057));
        }
    }

    /**
     * 查询栏目下是否存在内容
     * @param categoryId
     * @return
     */
    private boolean queryHasContent(String categoryId){
        int count = baseMapper.queryHasContent(categoryId);
        return count >0;
    }

    private boolean checkHasChild(String categoryId){
        QueryWrapper<Category> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("parent_id",categoryId);
        return baseMapper.selectCount(queryWrapper)>0;
    }

    private void saveUpdatetAttr(CategoryDto v,boolean save){
        String  attrData ="";
        if(Checker.BeNotBlank(v.getCategoryModelId())){
            List<DynamicModel> dynamicModels= getModelField(v);
            attrData = ModelFieldUtil.modelToJsonStr(dynamicModels);
        }
        if(save){
            CategoryAttrDto categoryAttrDto =new CategoryAttrDto();
            categoryAttrDto.setCategoryId(v.getId()).setData(attrData);
            categoryAttrService.insert(categoryAttrDto);
        }
        else{
            categoryAttrService.updateByCategory(v.getId(),attrData);
        }
    }

    private void filterMap(List<DynamicModel> dynamicModels,Map<String,Object> params){
        ModelFieldUtil.filterMapToSetFieldValue(dynamicModels,params);
    }

    private void checkLegal(CategoryDto v){
           Integer count = baseMapper.checkMaxCode(v.getSiteId(),v.getCode(), v.getId());
           if(count>0){
               throw new CustomException(ApiResult.result(5030));
           }
           if(Checker.BeBlank(v.getId())){
               v.setId(id());
           }
           if(v.getOnlyUrl()){
               v.setPathRule("").setPath("").setTemplatePath("").setPageSize(0).setTopPages(0);
           }
    }

    private  List<DynamicModel> getModelField(CategoryDto v){
        List<DynamicModel> dynamicModels = null;
        String fieldJson = categoryModelService.getDesignField(v.getCategoryModelId());
       if(Checker.BeNotBlank(fieldJson)){
           dynamicModels = ModelFieldUtil.listExtendModel(fieldJson);
       }
        List<DynamicModel> resultFields =Checker.BeNotEmpty(dynamicModels)?dynamicModels:Lists.newArrayList();
        filterMap(resultFields,v.getParams());
        return resultFields;
    }

    @Override
    public List<KeyValueModel> loadPathRule() {
        return PathRule.keyValues();
    }

    public static void main(String[] args) {
        List<Integer> ids=new ArrayList<Integer>();
        for(int i=1;i<=3989;i++){
            ids.add(i);
        }
        if(ids.size()>0){
            int countNum = ids.size();//总数
            if(countNum>1000){
                int multipleNum=countNum/1000;//倍数
                int remainderNum=countNum%1000;//余数
                int i=0;
                for(;i<multipleNum;i++){
                    System.out.println(ids.subList(i*1000, (i+1)*1000));
                }
                if(remainderNum>0){
                    System.out.println(ids.subList(i*1000, (i*1000+remainderNum)));
                }

            }else{
                System.out.println(ids);
            }
        }
    }
}
